use std::sync::Arc;
use actix_web::{web, HttpResponse};
use log::error;
use sea_orm::{DatabaseConnection, DatabaseTransaction, TransactionTrait};

use policy::policy_error::policy_error::PolicyError;

/// Handles a database transaction with the provided operation.
///
/// This function wraps a database operation in a transaction, handling the transaction lifecycle
/// (begin, commit, rollback) and error cases automatically.
///
/// # Arguments
/// * `db` - A reference to the database connection wrapped in web::Data
/// * `operation` - A closure that takes a DatabaseTransaction and returns a Future
///                 The Future should resolve to a Result containing an HttpResponse and the transaction
///
/// # Returns
/// * `HttpResponse` - The HTTP response from the operation if successful, or an error response
///                   if the transaction fails
///
/// # Error Handling
/// * Returns 500 Internal Server Error if transaction cannot be started or committed
/// * Returns the error status code from PolicyError if the operation fails
pub async fn handle_transaction<F, Fut>(db: web::Data<Arc<DatabaseConnection>>, operation: F) -> HttpResponse
where
    F: FnOnce(DatabaseTransaction) -> Fut,
    Fut: std::future::Future<Output = Result<(HttpResponse, DatabaseTransaction), PolicyError>>,
{
    let txn = match db.begin().await {
        Ok(txn) => txn,
        Err(e) => {
            error!("Failed to start transaction: {:?}", e);
            return HttpResponse::InternalServerError()
                .json(serde_json::json!({ "message": "Database transaction error" }));
        }
    };

    let result = operation(txn).await;

    match result {
        Ok((response, txn)) => {
            if let Err(e) = txn.commit().await {
                error!("Failed to commit transaction: {:?}", e);
                return HttpResponse::InternalServerError()
                    .json(serde_json::json!({ "message": "Failed to commit transaction" }));
            }
            response
        },
        Err(err) => {
            HttpResponse::build(err.status_code())
                .json(serde_json::json!({ "message": err.message() }))
        }
    }
}
